# routes_paypal.py
from flask import Blueprint, request, jsonify, current_app
from flask_login import login_required, current_user
from app.extensions import db
from app.models import Order, PaymentIntent, Product
from app.utils.orders_utils import gen_order_no
from datetime import datetime
from app.service.paypal import create_order as pp_create, capture_order as pp_capture, PaypalError
from sqlalchemy.exc import IntegrityError

paypal_bp = Blueprint('paypal', __name__, url_prefix='/api/paypal')


def gen_order_no() -> str:
    # 例：20251105-随机6位；你也可以换成雪花ID
    from random import randint
    return datetime.utcnow().strftime("%Y%m%d") + "-" + str(randint(100000, 999999))


def _verify_paypal_signature(req) -> bool:
    """
    最小可用占位：正式环境建议使用 PayPal 官方 Webhook 验证流程。
    如果你已在开发环境，先返回 True 方便联调。
    文档关键头部：
      PAYPAL-TRANSMISSION-ID
      PAYPAL-TRANSMISSION-TIME
      PAYPAL-TRANSMISSION-SIG
      PAYPAL-CERT-URL
      PAYPAL-AUTH-ALGO
    """
    # TODO: 上线前请改成官方验签；沙箱可先跳过
    return True


def _extract_capture(payload: dict):
    """
    从事件里抽取 capture 相关字段。
    1) PAYMENT.CAPTURE.COMPLETED: resource 就是 capture
    2) CHECKOUT.ORDER.APPROVED: 没有 capture，不处理入账
    """
    event_type = payload.get("event_type")
    resource = payload.get("resource", {})
    if event_type == "PAYMENT.CAPTURE.COMPLETED":
        cap_id = resource.get("id")
        # 通过 links 里的 up/self 可以回溯到 order id；不同版本里也可能在 supplemental_data 中
        pp_order_id = None
        for lk in resource.get("links", []):
            if lk.get("rel") in ("up", "up:order", "order"):  # 兼容写法
                # lk.href 类似 https://api-m.sandbox.paypal.com/v2/checkout/orders/XXXX
                href = lk.get("href") or ""
                if "/orders/" in href:
                    pp_order_id = href.split("/orders/")[-1]
                    break

        amount = (resource.get("amount") or {}).get("value")
        currency = (resource.get("amount") or {}).get("currency_code")
        status = resource.get("status")  # COMPLETED
        return {
            "event_type": event_type,
            "paypal_capture_id": cap_id,
            "paypal_order_id": pp_order_id,
            "amount_value": amount,
            "currency": currency,
            "status": status,
        }

    # 其他事件类型在这里按需扩展；未识别返回 None
    return None


@paypal_bp.route('/create-order', methods=['POST'])
@login_required
def paypal_create_order():
    data = request.json or {}
    order_id = data.get('order_id')
    order = Order.query.filter_by(id=order_id, user_id=current_user.id).first_or_404()

    try:
        # 以你本地订单号作 reference_id 便于回溯
        pp_order_id = pp_create(order.amount, order.currency, f"ORD-{order.id}")
        order.paypal_order_id = pp_order_id
        db.session.commit()
        return jsonify({'orderID': pp_order_id})
    except PaypalError as e:
        current_app.logger.exception(e)
        return jsonify({'error': str(e)}), 400


@paypal_bp.route('/capture', methods=['POST'])
@login_required
def paypal_capture():
    data = request.json or {}
    order_id = data.get('order_id')
    pp_order_id = data.get('orderID')
    order = Order.query.filter_by(id=order_id, user_id=current_user.id).first_or_404()

    # 基本一致性校验
    if order.paypal_order_id and order.paypal_order_id != pp_order_id:
        return jsonify({'error': 'orderID mismatch'}), 400

    # 幂等保护
    if order.status == 'paid':
        if order.product_id:
            product = Product.query.get(order.product_id)
            if product and product.stock is not None and product.stock > 0:
                product.stock = max(product.stock - 1, 0)
        db.session.commit()
        return jsonify({'status': 'ok', 'order_no': order.order_no})

    try:
        cap = pp_capture(pp_order_id)
        # 读取 capture id / 金额确认
        # PayPal 返回结构里通常在 purchase_units[].payments.captures[] 里
        captures = cap.get('purchase_units', [])[0].get('payments', {}).get('captures', [])
        if not captures:
            return jsonify({'error': 'no capture found'}), 400
        cap_id = captures[0]['id']
        # 可选再比对 amount
        # cap_amount = captures[0]['amount']  # {'currency_code':'USD','value':'xx.xx'}

        order.paypal_capture_id = cap_id
        order.status = 'paid'
        order.paid_at = datetime.utcnow()
        if not order.order_no:
            order.order_no = gen_order_no()
        db.session.commit()
        return jsonify({'status': 'ok', 'order_no': order.order_no})
    except PaypalError as e:
        current_app.logger.exception(e)
        return jsonify({'error': str(e)}), 400
    except IntegrityError:
        db.session.rollback()
        # 二次提交导致唯一约束冲突时的兜底
        return jsonify({'status': 'ok', 'order_no': order.order_no})


@paypal_bp.route('/webhook/paypal', methods=['POST'])
def paypal_webhook():
    # 1) 验签（上线务必开启）
    if not _verify_paypal_signature(request):
        return "invalid signature", 400

    payload = request.get_json(force=True, silent=True) or {}
    event_type = payload.get("event_type")
    info = _extract_capture(payload)

    # 对于 APPROVED 这类非资金落地事件，直接 200
    if event_type == "CHECKOUT.ORDER.APPROVED":
        current_app.logger.info("[PayPal] ORDER.APPROVED received (no capture) - %s",
                                payload.get("resource", {}).get("id"))
        return "", 200

    # 仅处理 capture 完成的入账
    if not info or info["event_type"] != "PAYMENT.CAPTURE.COMPLETED":
        current_app.logger.info("[PayPal] ignore event: %s", event_type)
        return "", 200

    cap_id = info["paypal_capture_id"]
    pp_order_id = info["paypal_order_id"]
    currency = info["currency"]
    # amount_str = info["amount_value"]  # 如需对账金额，可用 Decimal 校验

    # 2) 幂等：如果已有订单用此 capture 入账过，直接返回 200
    if cap_id:
        existed = Order.query.filter_by(paypal_capture_id=cap_id).first()
        if existed:
            current_app.logger.info("[PayPal] duplicate webhook for capture %s -> order %s", cap_id, existed.id)
            return "", 200

    # 3) 先尝试通过 paypal_order_id 找到既有订单（形态 A）
    order = None
    if pp_order_id:
        order = Order.query.filter_by(paypal_order_id=pp_order_id).first()

    # 4) 如果没找到订单，则尝试在 PaymentIntent 里做对账（形态 B）
    if not order:
        pi = None
        if pp_order_id:
            pi = PaymentIntent.query.filter_by(paypal_order_id=pp_order_id).first()
        if not pi and cap_id:
            pi = PaymentIntent.query.filter_by(paypal_capture_id=cap_id).first()

        if pi:
            # 标记 Intent 已经入账
            pi.status = "captured"
            if cap_id:
                pi.paypal_capture_id = cap_id
            db.session.add(pi)
            db.session.commit()

            # ✅ 这里可以“自动建单”（可选）
            # 由于 PaymentIntent 当前模型里没有 user_id / 收件信息，
            # 你可以在 PaymentIntent.biz_ref 里携带一个可回溯的 ID（比如临时下单号/用户ID），
            # 再据此从你的临时表或 session 里取收件信息与购物车。
            #
            # 伪代码：
            # user_id, recipient, items, amount_cents, currency = load_draft_from_biz_ref(pi.biz_ref)
            # order = Order(
            #     user_id=user_id, amount=amount_cents, currency=currency,
            #     recipient_name=recipient['name'], recipient_email=recipient['email'],
            #     recipient_phone=recipient['phone'], recipient_address=recipient['address'],
            #     recipient_postal_code=recipient['postal_code'],
            #     paypal_order_id=pp_order_id, paypal_capture_id=cap_id,
            #     status="paid", fulfillment_status="to_ship", paid_at=datetime.utcnow(),
            #     order_no=gen_order_no()
            # )
            # db.session.add(order); db.session.flush()
            # for it in items: db.session.add(OrderItem(...))
            # db.session.commit()
            #
            # 如暂不自动建单，这里直接 200 结束，由后台“对账页”人工一键生成订单。
            current_app.logger.info("[PayPal] captured PaymentIntent %s (biz_ref=%s). Waiting for order creation.",
                                    pi.id, pi.biz_ref)
            return "", 200

    # 5) 找到订单 → 标记为已支付（幂等）
    if order:
        order.status = "paid"
        order.paypal_capture_id = cap_id or order.paypal_capture_id
        # 可选金额/币种校验（建议在生产开启，校验失败记录告警）
        # if currency and currency != order.currency: ...
        if not order.order_no:
            order.order_no = gen_order_no()
        if not order.paid_at:
            order.paid_at = datetime.utcnow()
        db.session.add(order)
        db.session.commit()
        current_app.logger.info("[PayPal] order paid: id=%s no=%s cap=%s", order.id, order.order_no, cap_id)
        return "", 200

    # 6) 既没有匹配到 Order，也没有匹配到 PaymentIntent（或未携带信息）
    # 记录一条日志方便排查；返回 200，避免 PayPal 重试风暴
    current_app.logger.warning("[PayPal] capture without local match: order_id=%s capture_id=%s", pp_order_id, cap_id)
    return "", 200